package com.example.javapoet.demo.scenarios;

import com.squareup.javapoet.*;
import org.junit.jupiter.api.*;
import org.mockito.*;
import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * JavaPoet场景示例 - 生成测试代码
 */
public class TestCodeGeneration {

    public static void main(String[] args) {
        // 生成单元测试类
        generateUnitTestClass();
        
        // 生成集成测试类
        generateIntegrationTestClass();
        
        // 生成Mock测试类
        generateMockTestClass();
    }

    /**
     * 生成单元测试类
     */
    public static void generateUnitTestClass() {
        // 创建测试方法
        MethodSpec testAddMethod = MethodSpec.methodBuilder("testAdd")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T calculator = new $T()", 
                        ClassName.get("com.example.service", "Calculator"),
                        ClassName.get("com.example.service", "Calculator"))
                .addStatement("$T result = calculator.add(2, 3)", int.class)
                .addStatement("$T.assertEquals(5, result)", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .build();
        
        MethodSpec testSubtractMethod = MethodSpec.methodBuilder("testSubtract")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T calculator = new $T()", 
                        ClassName.get("com.example.service", "Calculator"),
                        ClassName.get("com.example.service", "Calculator"))
                .addStatement("$T result = calculator.subtract(5, 3)", int.class)
                .addStatement("$T.assertEquals(2, result)", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .build();
        
        MethodSpec testDivideMethod = MethodSpec.methodBuilder("testDivide")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addException(Exception.class)
                .addStatement("$T calculator = new $T()", 
                        ClassName.get("com.example.service", "Calculator"),
                        ClassName.get("com.example.service", "Calculator"))
                .addStatement("$T result = calculator.divide(10, 2)", double.class)
                .addStatement("$T.assertEquals(5.0, result, 0.001)", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .build();
        
        MethodSpec testDivideByZeroMethod = MethodSpec.methodBuilder("testDivideByZero")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T calculator = new $T()", 
                        ClassName.get("com.example.service", "Calculator"),
                        ClassName.get("com.example.service", "Calculator"))
                .addStatement("$T exception = $T.assertThrows($T.class, () -> calculator.divide(10, 0))",
                        ClassName.get("java.lang", "ArithmeticException"),
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        ClassName.get("java.lang", "ArithmeticException"))
                .addStatement("$T.assertEquals($S, exception.getMessage())",
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "/ by zero")
                .build();
        
        // 创建测试类
        TypeSpec calculatorTestClass = TypeSpec.classBuilder("CalculatorTest")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(DisplayName.class)
                        .addMember("value", "$S", "Calculator Unit Tests")
                        .build())
                .addMethod(testAddMethod)
                .addMethod(testSubtractMethod)
                .addMethod(testDivideMethod)
                .addMethod(testDivideByZeroMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.service", calculatorTestClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成CalculatorTest类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/test/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成CalculatorTest.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成集成测试类
     */
    public static void generateIntegrationTestClass() {
        // 创建测试方法
        MethodSpec testUserCreationMethod = MethodSpec.methodBuilder("testUserCreation")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .addAnnotation(AnnotationSpec.builder(DisplayName.class)
                        .addMember("value", "$S", "Test user creation flow")
                        .build())
                .returns(void.class)
                .addStatement("$T userService = new $T()", 
                        ClassName.get("com.example.service", "UserService"),
                        ClassName.get("com.example.service", "UserService"))
                .addStatement("$T user = new $T($S, $S)", 
                        ClassName.get("com.example.model", "User"),
                        ClassName.get("com.example.model", "User"),
                        "testUser", "test@example.com")
                .addStatement("$T savedUser = userService.createUser(user)", 
                        ClassName.get("com.example.model", "User"))
                .addStatement("$T.assertNotNull(savedUser.getId())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .addStatement("$T.assertEquals($S, savedUser.getName())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "testUser")
                .addStatement("$T.assertEquals($S, savedUser.getEmail())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "test@example.com")
                .build();
        
        MethodSpec testUserUpdateMethod = MethodSpec.methodBuilder("testUserUpdate")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T userService = new $T()", 
                        ClassName.get("com.example.service", "UserService"),
                        ClassName.get("com.example.service", "UserService"))
                .addStatement("$T user = new $T($S, $S)", 
                        ClassName.get("com.example.model", "User"),
                        ClassName.get("com.example.model", "User"),
                        "testUser", "test@example.com")
                .addStatement("$T savedUser = userService.createUser(user)", 
                        ClassName.get("com.example.model", "User"))
                .addStatement("savedUser.setName($S)", "updatedUser")
                .addStatement("savedUser.setEmail($S)", "updated@example.com")
                .addStatement("$T updatedUser = userService.updateUser(savedUser)", 
                        ClassName.get("com.example.model", "User"))
                .addStatement("$T.assertEquals($S, updatedUser.getName())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "updatedUser")
                .addStatement("$T.assertEquals($S, updatedUser.getEmail())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "updated@example.com")
                .build();
        
        // 创建测试类
        TypeSpec userServiceIntegrationTestClass = TypeSpec.classBuilder("UserServiceIntegrationTest")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(TestInstance.class)
                        .addMember("value", "$T.Lifecycle.PER_CLASS", TestInstance.Lifecycle.class)
                        .build())
                .addAnnotation(AnnotationSpec.builder(DisplayName.class)
                        .addMember("value", "$S", "User Service Integration Tests")
                        .build())
                .addMethod(testUserCreationMethod)
                .addMethod(testUserUpdateMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.service", userServiceIntegrationTestClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成UserServiceIntegrationTest类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/test/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成UserServiceIntegrationTest.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成Mock测试类
     */
    public static void generateMockTestClass() {
        // 创建字段
        FieldSpec userRepositoryField = FieldSpec.builder(
                ClassName.get("com.example.repository", "UserRepository"), 
                "userRepository")
                .addModifiers(Modifier.PRIVATE)
                .addAnnotation(Mock.class)
                .build();
        
        FieldSpec userServiceField = FieldSpec.builder(
                ClassName.get("com.example.service", "UserService"), 
                "userService")
                .addModifiers(Modifier.PRIVATE)
                .addAnnotation(InjectMocks.class)
                .build();
        
        // 创建setup方法
        MethodSpec setupMethod = MethodSpec.methodBuilder("init")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(BeforeEach.class)
                .returns(void.class)
                .addStatement("$T.initMocks(this)", MockitoAnnotations.class)
                .build();
        
        // 创建测试方法
        MethodSpec testGetUserByIdMethod = MethodSpec.methodBuilder("testGetUserById")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T user = new $T(1L, $S, $S)", 
                        ClassName.get("com.example.model", "User"),
                        ClassName.get("com.example.model", "User"),
                        "testUser", "test@example.com")
                .addStatement("$T.when(userRepository.findById(1L)).thenReturn($T.of(user))", 
                        Mockito.class, Optional.class)
                .addStatement("$T foundUser = userService.getUserById(1L)", 
                        ClassName.get("com.example.model", "User"))
                .addStatement("$T.assertNotNull(foundUser)", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .addStatement("$T.assertEquals($S, foundUser.getName())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"),
                        "testUser")
                .addStatement("$T.verify(userRepository, $T.times(1)).findById(1L)", 
                        Mockito.class, Mockito.class)
                .build();
        
        MethodSpec testCreateUserMethod = MethodSpec.methodBuilder("testCreateUser")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Test.class)
                .returns(void.class)
                .addStatement("$T user = new $T($S, $S)", 
                        ClassName.get("com.example.model", "User"),
                        ClassName.get("com.example.model", "User"),
                        "newUser", "new@example.com")
                .addStatement("$T savedUser = new $T(1L, $S, $S)", 
                        ClassName.get("com.example.model", "User"),
                        ClassName.get("com.example.model", "User"),
                        "newUser", "new@example.com")
                .addStatement("$T.when(userRepository.save(user)).thenReturn(savedUser)", 
                        Mockito.class)
                .addStatement("$T result = userService.createUser(user)", 
                        ClassName.get("com.example.model", "User"))
                .addStatement("$T.assertEquals(1L, result.getId())", 
                        ClassName.get("org.junit.jupiter.api", "Assertions"))
                .addStatement("$T.verify(userRepository, $T.times(1)).save(user)", 
                        Mockito.class, Mockito.class)
                .build();
        
        // 创建测试类
        TypeSpec userServiceMockTestClass = TypeSpec.classBuilder("UserServiceMockTest")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(DisplayName.class)
                        .addMember("value", "$S", "User Service Mock Tests")
                        .build())
                .addField(userRepositoryField)
                .addField(userServiceField)
                .addMethod(setupMethod)
                .addMethod(testGetUserByIdMethod)
                .addMethod(testCreateUserMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.service", userServiceMockTestClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成UserServiceMockTest类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/test/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成UserServiceMockTest.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}